<?php


namespace App\Admin\Services\App;


use App\Admin\Exceptions\App\Project\ProjectException;
use App\Admin\Exceptions\Wechat\MiniProgramTemplateException;
use App\Admin\Requests\App\Project\ProjectUpdateAuditItemListRequest;
use App\Commons\WechatOpenPlatform\WechatOpenPlatformTool;
use App\Http\Middleware\TerminateTask;
use App\Models\App\AppProject;
use App\Models\Wechat\MiniProgramOperationRecord;
use App\Repositories\App\AppProjectRepository;
use App\Repositories\Wechat\MiniProgramOperationRecordRepository;
use App\Repositories\Wechat\MiniProgramTemplateRepository;
use Illuminate\Support\Facades\Auth;

class ProjectService {
    /**
     * @return mixed
     */
    public function getProjectPage() {
        $pageInfo = AppProjectRepository::getInstance()->queryByConditionAndPage(function ($query) {
            $query->with(['appProjectType', 'miniProgramTemplate','user']);
        });

        foreach ($pageInfo['list'] as $pageInfoListIndex => $itemEl) {
            $pageInfo['list'][$pageInfoListIndex]['app_project_type_name']      = $itemEl->appProjectType ? $itemEl->appProjectType->name : '';
            $pageInfo['list'][$pageInfoListIndex]['mini_program_template_name'] = $itemEl->miniProgramTemplate ? $itemEl->miniProgramTemplate->name : '';
            $pageInfo['list'][$pageInfoListIndex]['add_user_name']              = $itemEl->user ? $itemEl->user->name : '';
        }

        return $pageInfo;
    }

    /**
     * 新建新的项目
     *
     * @param $data
     * @param $fillAble
     * @return \Illuminate\Database\Eloquent\Model
     * @throws \Exception
     */
    public function projectStore($data, $fillAble) {
        //继承代码模板的域名配置和审核项列表
        $templateEl = (new MiniProgramTemplateRepository())->findItemElById($data['mpt_id'], new MiniProgramTemplateException(1100001));
        $templateData = [
            'server_domain' => array_map(
                function ($val) {
                    return [];
                },
                $templateEl->server_domain
            ),
            'webviewdomain' => [],
            'wx_submit_audit_item_list' => $templateEl->wx_submit_audit_item_list,
        ];
        $data = array_merge($data, $templateData);
        $fillAble = array_merge($fillAble, array_keys($templateData));
        $data['user_id'] = Auth::user()->id;
        $fillAble[]      = 'user_id';
        $projectEl = AppProjectRepository::getInstance()->store($data, $fillAble);
        $this->_recordOperation('新建小程序项目', $projectEl, $data);

        return $projectEl;
    }

    /**
     * @param $id
     * @param $data
     * @param $fillAble
     *
     * @return \Illuminate\Database\Eloquent\Model|mixed
     * @throws \Exception
     */
    public function projectUpdate($id, $data, $fillAble) {
        $projectEl = AppProjectRepository::getInstance()->update($id, $data, $fillAble, new ProjectException(1300002));
        $this->_recordOperation('修改小程序项目', $projectEl, $data);

        return $projectEl;
    }

    /**
     * @param int $id
     *
     * @return \Illuminate\Database\Eloquent\Model|mixed
     * @throws \Exception
     */
    public function projectDelete(int $id) {
        $projectEl = AppProjectRepository::getInstance()->delete($id);
        $this->_recordOperation('删除小程序项目', $projectEl, ['id' => $id]);

        return $projectEl;
    }

    /**
     * 获取授权连接
     *
     * @param $id
     *
     * @return array
     * @throws \Exception
     */
    public function getAuthLoginPageUrl($id) {
        $projectEl      = $this->getProjectItemElById($id);
        $param          = [
            'id' => $id,
        ];
        $callbackUrl    = config('app.url') . '/api/open/wechat/authorization-callback?' . http_build_query($param);
        $projectAuthUrl = WechatOpenPlatformTool::getInstance()->getComponentLoginPageUrl($callbackUrl);
        $returnResult   = [
            'id'                       => $id,
            'callback_url'             => $callbackUrl,
            'component_login_page_url' => $projectAuthUrl,
        ];
        $this->_recordOperation('获取授权连接', $projectEl, $returnResult);

        return $returnResult;
    }

    /**
     * 小程序发布处理
     *
     * @param $id
     *
     * @return null
     * @throws \Exception
     */
    public function publishMiniProgram($id) {
        $projectEl = $this->getProjectItemElById($id, function ($query) {
            $query->with(['appProjectType', 'miniProgramTemplate']);
        });

        return self::publishMiniProgramByProjectEl($projectEl);
    }

    /**
     * 执行项目 上传小程序代码并提交审核
     *
     * @param $projectEl
     *
     * @return array
     * @throws ProjectException
     * @throws \Exception
     */
    public static function publishMiniProgramByProjectEl($projectEl) {
        $miniProgramTool   = self::getMiniProgramToolByProjectEl($projectEl);
        $commitResult      = self::miniProgramCodeCommitByProjectEl($projectEl, $miniProgramTool);
        $submitAuditResult = self::miniProgramSubmitAuditByProjectEl($projectEl, $miniProgramTool);

        return $commitResult + $submitAuditResult;
    }

    /**
     * 获取体验版二维码
     *
     * @param $id
     *
     * @return \EasyWeChat\Kernel\Http\Response|mixed
     * @throws \Exception
     */
    public function getExperienceQrCode($id) {
        $projectEl = $this->getProjectItemElById($id);

        return self::getMiniProgramToolByProjectEl($projectEl)->getQrCode();
    }

    /**
     * 操作记录
     *
     * @param $projectEl
     */
    protected function _recordOperation($opName, $projectEl, $resultData = null, $opResult = MiniProgramOperationRecord::OP_RESULT_SUCCESS) {
        return MiniProgramOperationRecordRepository::getInstance()->store([
            'user_id'             => Auth::check() ? Auth::user()->id : 0,
            'ap_id'               => $projectEl ? $projectEl->id : 0,
            'open_appid'          => $projectEl->open_appid,
            'wx_authorizer_appid' => $projectEl->wx_authorizer_appid,
            'op_name'             => $opName,
            'result'              => json_encode($resultData),
            'op_result'           => $opResult,
            'mpt_id'              => $projectEl->mpt_id,
        ]);
    }

    /**
     * 同步服务器域名配置
     *
     * @param $id
     *
     * @return mixed
     * @throws \Exception
     */
    public function synServerDomain($id) {
        $projectEl       = $this->getProjectItemElById($id);
        $domainSetParams = $projectEl->serverDomainWithTypeAndTemplate() + ['action' => 'set'];

        return self::getMiniProgramToolByProjectEl($projectEl)->domainModify($domainSetParams);
    }

    /**
     * 同步业务域名配置
     *
     * @param $id
     *
     * @return mixed
     * @throws \Exception
     */
    public function synWebviewDomain($id) {
        $projectEl = $this->getProjectItemElById($id);

        return self::getMiniProgramToolByProjectEl($projectEl)->setWebviewDomain($projectEl->webviewdomainWithTypeAndTemplate(), 'set');
    }

    /**
     * 修改服务器域名和业务域名
     *
     * @param $id
     * @param $data
     * @param $fillAble
     *
     * @throws \Exception
     */
    public function updateDomain($id, $data, $fillAble) {
        $projectEl = $this->projectUpdate($id, $data, $fillAble);

        TerminateTask::addTaskFnList(function () use ($projectEl) {
            self::getMiniProgramToolByProjectEl($projectEl)->domainModify($projectEl->serverDomainWithTypeAndTemplate() + ['action' => 'set']);
        });

        TerminateTask::addTaskFnList(function () use ($projectEl) {
            self::getMiniProgramToolByProjectEl($projectEl)->setWebviewDomain($projectEl->webviewdomainWithTypeAndTemplate(), 'set');
        });

        return $projectEl;
    }

    /**
     * @param      $id
     * @param bool $isThrowException
     *
     * @return \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Builder[]|\Illuminate\Database\Eloquent\Collection|\Illuminate\Database\Eloquent\Model|null
     * @throws \Exception
     */
    public function getProjectItemElById($id, $queryExtDealFn = null, $isThrowException = true) {
        return AppProjectRepository::getInstance()->findItemElById($id, $isThrowException ? (new ProjectException(1300002)) : null, $queryExtDealFn);
    }

    /**
     * 版本回退
     *
     * @param $id
     *
     * @return mixed
     * @throws \Exception
     */
    public function revertCodeRelease($id) {
        $projectEl = $this->getProjectItemElById($id);

        return self::getMiniProgramToolByProjectEl($projectEl)->rollbackRelease();
    }

    /**
     * 查询最新一次提交的审核状态
     *
     * @param $id
     *
     * @return mixed
     * @throws \Exception
     */
    public function getLatestAuditStatus($id) {
        $projectEl = $this->getProjectItemElById($id);

        $getLatestAuditStatusResult = self::getMiniProgramToolByProjectEl($projectEl)->getLatestAuditStatus();

        if(isset($getLatestAuditStatusResult['status'])){
            $projectEl->last_audit_status = $getLatestAuditStatusResult['status'];
            $projectEl->save();
        }

        return $getLatestAuditStatusResult;
    }

    /**
     * 上传小程序代码
     *
     * @param $id
     *
     * @return array|\EasyWeChat\Kernel\Support\Collection|object|\Psr\Http\Message\ResponseInterface|string
     * @throws ProjectException
     * @throws \Exception
     */
    public function codeCommit($id) {
        return self::miniProgramCodeCommitByProjectEl($this->getProjectItemElById($id));
    }

    /**
     * 小程序提交审核
     *
     * @param $id
     *
     * @return array
     * @throws \Exception
     */
    public function miniProgramSubmitAudit($id) {
        return self::miniProgramSubmitAuditByProjectEl($this->getProjectItemElById($id));
    }

    /**
     * 小程序审核撤回
     *
     * @param $id
     *
     * @return mixed
     * @throws \Exception
     */
    public function miniProgramWithdrawAudit($id) {
        $projectEl           = $this->getProjectItemElById($id);
        $withdrawAuditResult = self::getMiniProgramToolByProjectEl($projectEl)->withdrawAudit();

        $projectEl->last_audit_status = AppProject::LAST_AUDIT_STATUS_UNDO;
        $projectEl->save();

        return $withdrawAuditResult;
    }

    /**
     * 发布已通过审核的小程序
     *
     * @param $id
     *
     * @return mixed
     * @throws \Exception
     */
    public function miniProgramRelease($id) {
        return self::getMiniProgramToolByProjectEl($this->getProjectItemElById($id))->release();
    }

    /**
     * @param $projectEl
     *
     * @return \App\Commons\WechatOpenPlatform\WechatOpenPlatformMiniProgramTool
     */
    public static function getMiniProgramToolByProjectEl($projectEl) {
        return WechatOpenPlatformTool::getInstance()
            ->getMiniProgramTool($projectEl->wx_authorizer_appid, $projectEl->wx_authorizer_refresh_token, $projectEl);
    }

    /**
     * 修改审核项列表
     *
     * @param $id
     * @param ProjectUpdateAuditItemListRequest $request
     * @return \Illuminate\Database\Eloquent\Model|mixed
     * @throws \Exception
     */
    public function updateAuditItemList($id, ProjectUpdateAuditItemListRequest $request) {
        return AppProjectRepository::getInstance()->update($id, $request->post(), $request->getRuleKeys(), new ProjectException(1300002));
    }

    /**
     * 上传小程序代码
     *
     * @param      $projectEl
     * @param null $miniProgramTool
     *
     * @return array|\EasyWeChat\Kernel\Support\Collection|object|\Psr\Http\Message\ResponseInterface|string
     * @throws ProjectException
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
     */
    public static function miniProgramCodeCommitByProjectEl($projectEl, $miniProgramTool = null) {
        //检测是否授权了小程序
        if ($projectEl->auth_status == AppProject::AUTH_STATUS_NO) {
            throw new ProjectException(1300001);
        }

        $templateId  = $projectEl->miniProgramTemplate->wx_template_id;
        $version     = $projectEl->miniProgramTemplate->wx_user_version;
        $description = $projectEl->miniProgramTemplate->wx_user_desc;

        //extJson组织
        $extJson = array_replace_recursive(
            WechatOpenPlatformTool::getOpenPlatformConfigWechatMiniProgramCommonExtJson(),
            [
                'ext' => [
                    'open_appid'                      => $projectEl->open_appid,
                    'open_wx_authorizer_appid'        => $projectEl->wx_authorizer_appid,
                    'open_commit_at'                  => date('Y-m-d H:i:s'),
                    'open_commit_version'             => $version,
                    'open_commit_template_id'         => $templateId,
                    'open_commit_description'         => $description,
                    'open_commit_template_created_at' => $projectEl->miniProgramTemplate->wx_created_at,
                ],
            ],
            $projectEl->appProjectType->ext_json,
            $projectEl->miniProgramTemplate->ext_json,
            $projectEl->ext_json
        );

        is_null($miniProgramTool) && ($miniProgramTool = self::getMiniProgramToolByProjectEl($projectEl));

        //上传代码
        $commitResult = $miniProgramTool->miniProgramTemplateCommit($templateId, json_encode($extJson), $version, $description);

        return [
            'ext_json'      => $extJson,
            'commit_result' => $commitResult,
        ];
    }

    /**
     * 小程序提交审核,在调用上传代码接口为小程序上传代码后，可以调用本接口，将上传的代码提交审核。
     *
     * @param      $projectEl
     * @param null $miniProgramTool
     *
     * @return array
     * @throws \Exception
     */
    public static function miniProgramSubmitAuditByProjectEl($projectEl, $miniProgramTool = null) {
        //检测是否授权了小程序
        if ($projectEl->auth_status == AppProject::AUTH_STATUS_NO) {
            throw new ProjectException(1300001);
        }

        is_null($miniProgramTool) && ($miniProgramTool = self::getMiniProgramToolByProjectEl($projectEl));

        //提交审核,注意：这里 有正在提交审核 的会有异常抛出
        $itemList          = $projectEl->wx_submit_audit_item_rely_mpt ? $projectEl->miniProgramTemplate->wx_submit_audit_item_list : $projectEl->wx_submit_audit_item_list;
        $submitAuditResult = $miniProgramTool->miniProgramSubmitAudit($itemList);

        $projectEl->last_audit_status = AppProject::LAST_AUDIT_STATUS_WAITING;
        $projectEl->save();

        return [
            'item_list'           => $itemList,
            'submit_audit_result' => $submitAuditResult,
        ];
    }

    /**
     * 加急审核申请
     *
     * @param $id
     * @param $auditId
     *
     * @return mixed
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
     */
    public function speedUpAudit($id, $auditId) {
        return self::getMiniProgramToolByProjectEl($this->getProjectItemElById($id))->speedUpAudit($auditId);
    }

    /**
     * 查询服务商的当月提审限额（quota）和加急次数
     *
     * @param $id
     *
     * @return mixed
     * @throws \Exception
     */
    public function getMonthSubmitLimitInfo($id) {
        return self::getMiniProgramToolByProjectEl($this->getProjectItemElById($id))->queryQuota();
    }

    /**
     * 查询小程序用户隐私保护指引
     * @param $id
     * @return array|\EasyWeChat\Kernel\Support\Collection|object|\Psr\Http\Message\ResponseInterface|string
     * @throws \Exception
     */
    public function getPrivacy($id) {
        return self::getMiniProgramToolByProjectEl($this->getProjectItemElById($id))->getPrivacy();
    }

    /**
     * 配置小程序用户隐私保护指引
     * @param $id
     * @param $param
     * @return array|\EasyWeChat\Kernel\Support\Collection|object|\Psr\Http\Message\ResponseInterface|string
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function setPrivacy($id,$param) {
        return self::getMiniProgramToolByProjectEl($this->getProjectItemElById($id))->setPrivacy($param);
    }
}
